import dateutil.parser

import coin.exchange.base.kr_rest.private_client_base as privcb
import coin.exchange.upbit_v1.kr_rest.native_private_client as nprivc

from coin.exchange.upbit_v1.kr_rest.constants import coin_list
from coin.exchange.upbit_v1.kr_rest.currency import UpbitCurrency
from coin.exchange.upbit_v1.kr_rest.product import (UpbitProduct)
from coin.proto.coin_query_pb2 import AccountBalance, CurrencyBalance

import coin.proto.coin_order_enums_pb2 as coin_order
import coin.proto.coin_query_pb2 as coin_query
from coin.proto.coin_query_pb2 import (
    AccountBalance,
    CurrencyBalance,
    ProductFillElement,
    ProductFillBundle,
    ProductOrderElement,
    ProductOrderBundle,
)


class UpbitPrivateParser(object):
  currencies = coin_list + ['KRW']

  @staticmethod
  def parse_balance(update_msg):
    account_balance = AccountBalance()
    account_balance.exchange = 'Upbit'

    for update_elem in update_msg:
      currency = UpbitCurrency.FromStrNativeCurrencyNothrow(update_elem["currency"])
      if currency is None:
        continue
      currency_balance = CurrencyBalance()
      currency_balance.currency = currency.currency
      currency_balance.currency_native = currency.native_currency
      currency_balance.available = float(update_elem['balance'])
      currency_balance.hold = float(update_elem['locked'])
      currency_balance.total = currency_balance.available + currency_balance.hold
      account_balance.each_balance.extend([currency_balance])
    return account_balance

  @staticmethod
  def parse_fill(update_msg, product):
    currency_fill_bundle = ProductFillBundle()
    currency_fill_bundle.exchange = 'Upbit'
    currency_fill_bundle.symbol = product.symbol
    for update_elem in update_msg:
      if update_elem["state"] != "done":
        continue
      if update_elem["market"] != product.native_symbol:
        continue
      currency_fill = ProductFillElement()
      currency_fill.symbol = product.symbol
      currency_fill.price = float(update_elem['price'])
      currency_fill.qty = float(update_elem['volume'])
      currency_fill.fee = float(update_elem['paid_fee'])
      currency_fill.order_id = update_elem['uuid']
      currency_fill.fill_timestamp = int(
          dateutil.parser.parse(update_elem['created_at']).timestamp() * (10**9))
      if update_elem['side'] == 'ask':
        currency_fill.side = coin_order.SELL_ORDER
      elif update_elem['side'] == 'bid':
        currency_fill.side = coin_order.BUY_ORDER
      else:
        continue
      currency_fill_bundle.each_fill.extend([currency_fill])
    return currency_fill_bundle

  @staticmethod
  def parse_orders(update_msg, product):
    currency_fill_bundle = ProductOrderBundle()
    currency_fill_bundle.exchange = 'Upbit'
    currency_fill_bundle.symbol = product.symbol
    for update_elem in update_msg:
      if update_elem["state"] != "wait":
        continue
      if update_elem["market"] != product.native_symbol:
        continue
      currency_fill = ProductFillElement()
      currency_fill.symbol = product.symbol
      currency_fill.price = float(update_elem['price'])
      currency_fill.qty = float(update_elem['volume'])
      currency_fill.fee = float(update_elem['paid_fee'])
      currency_fill.order_id = update_elem['uuid']
      currency_fill.fill_timestamp = int(
          dateutil.parser.parse(update_elem['created_at']).timestamp() * (10**9))
      if update_elem['side'] == 'ask':
        currency_fill.side = coin_order.SELL_ORDER
      elif update_elem['side'] == 'bid':
        currency_fill.side = coin_order.BUY_ORDER
      else:
        continue
      currency_fill_bundle.each_fill.extend([currency_fill])
    return currency_fill_bundle


class UpbitPrivateClient(privcb.PrivateClientBase):
  ProductType = UpbitProduct

  def __init__(self, key_file, proxy=None):
    privcb.PrivateClientBase.__init__(self)
    self.nprivc = nprivc.UpbitNativePrivateClient(key_file=key_file, proxy=proxy)

  def query_account_balance_impl(self):
    update = self.nprivc.query_GET_accounts()
    update.msg = UpbitPrivateParser.parse_balance(update.msg)
    return update

  def query_fills_impl(self, product):
    symbol = product.native_symbol
    update = self.nprivc.query_GET_orders(market=symbol, state="done")
    update.msg = UpbitPrivateParser.parse_fill(update.msg, product)
    return update

  def query_list_orders_impl(self, product):
    symbol = product.native_symbol
    update = self.nprivc.query_GET_orders(market=symbol, state="wait")
    update.msg = UpbitPrivateParser.parse_orders(update.msg, product)
    return update

  def query_submit_impl(self, product, side, price, qty):
    symbol = product.native_symbol
    update = self.nprivc.query_POST_orders(market=symbol,
                                           ord_type="limit",
                                           side=side,
                                           price=price,
                                           volume=qty)
    # update.msg = UpbitPrivateParser.parse_orders(update.msg)
    return update

  def query_cancel_impl(self, product, order_id):
    symbol = product.native_symbol
    update = self.nprivc.query_DELETE_order(uuid=order_id)
    # update.msg = UpbitPrivateParser.parse_orders(update.msg)
    return update

  def query_cancel_product_impl(self, product):
    orders = self.query_list_orders_impl(product)
    for an_order in orders.msg.each_order:
      self.query_cancel_impl(product, an_order.order_id)
